#include "stm32f10x.h"                  // Device header
#include "Serial.h"
#include "Delay.h"
#include <string.h>
#include "SysRTC.h"
#include <stdio.h>

#define ACK_TIMEOUT 						3				// 模块响应超时时间（秒）
#define RST_TIMEOUT 						5				// 模块重启超时时间（秒）
#define CONNECT_TIMEOUT					10			// 连接超时时间（秒）
#define EXIT_DATA_MODE_DELAY		20			// 退出数据模式延时（毫秒）
#define FIRST_START_DELAY				2000		// 首次启动超时时间（毫秒）
#define TCP_RX_FRAME_HEAD	 			"+TRDTC:"  // tcp接收报文头


// 接收的数据
char rxData[RX_BYTE_BUFFER_SIZE];

/*
** 将字符转换为uint8，字符c表示的字面量的uint8值装载到data中
** 返回：1转换成功，0转换失败
*/
uint8_t charToU8(char c, uint8_t *data) {
	// 判断字符在 '0' ~ '9' 范围内
	if (c >= 0x30 && c <= 0x39) {
		*data = c - 0x30;
		return 1;
	}
	return 0;
}

/*
** 等待响应特定的字符串，指定超时时间
** 返回：1成功收到指定应答字符串，0超时未收到指定字符串
*/
uint8_t waitForAckStrTimeout(char *expected, uint8_t timeout) {
	// 开始的时间，用于计算超时
	time_t startPoint = SysRTC_GetTimestamp();
	// 如果还没超时，继续循环
	while ((SysRTC_GetTimestamp() - startPoint) < timeout) {
		if (Serial_hasRxData()) {
			Serial_readRxData(rxData);
			// 收到指定应答，返回1
			if (strcmp(expected, rxData) == 0) {
				return 1;
			}
		}
	}
	return 0;
}

/*
** 等待响应特定的字符串
** 返回：1成功收到指定应答字符串，0超时未收到指定字符串
*/
uint8_t waitForAckStr(char *expected) {
	return waitForAckStrTimeout(expected, ACK_TIMEOUT);
}

/*
** 等待响应uint8数据
** 参数：frameHead报文头，用于匹配报文; index为响应数据在数据报文中的位置偏移量; data为数据装载地址，如果有数据的话
*/
uint8_t waitForAckU8(char *frameHead, uint8_t index, uint8_t *data) {
	// 开始的时间，用于计算超时
	time_t startPoint = SysRTC_GetTimestamp();
	// 报文头长度
	size_t headLen = strlen(frameHead);
	// 如果还没超时，继续循环
	while ((SysRTC_GetTimestamp() - startPoint) < ACK_TIMEOUT) {
		if (Serial_hasRxData()) {
			Serial_readRxData(rxData);
			// 当前报文长度
			size_t frameLen = strlen(rxData);
			// 报文头比当前整个报文长度大，则直接跳过该报文
			if (headLen > frameLen) {
				continue;
			}
			for (size_t i = 0; i < frameLen; i++) {
				// 判断报文头，是否匹配，如果不匹配，则当前报文不匹配，直接退出当前循环，继续获取下一个报文
				if (i < headLen) {
					if (rxData[i] != frameHead[i]) {
						break;
					}
				} else {
					// 当前报文是匹配的报文，则进入取报文数据
					uint8_t argIndex = 0;
					for (size_t j = i; j < frameLen; j++) {
						// 要取的目标数据偏移量
						if (argIndex == index) {
							// 将当前字符转换为u8，如果转换成功，则返回1，否则返回0
							if (charToU8(rxData[j], data)) {
								return 1;
							}
							return 0;
						}
					}
				}
			}
		}
	}
	return 0;
}

/*
** 清空接收报文缓冲区
*/
void clearRxFrameBuffer(void) {
	while (Serial_hasRxData()) {
		Serial_readRxData(rxData);
	}
}

/*
** 发送报文
*/
void sendFrame(char *frame) {
	// 先清空接收报文缓冲区，以免干扰本次指令
	clearRxFrameBuffer();
	// 发送数据
	Serial_SendString(frame);
}

/*
** 等待wifi连接
** 返回：1连接成功，0超时
*/
uint8_t waitWifiConnected() {
	// 开始的时间，用于计算超时
	time_t startPoint = SysRTC_GetTimestamp();
	// 网络连接信息
	uint8_t wifiState = 0xFF;
	// 未超时，继续循环
	while ((SysRTC_GetTimestamp() - startPoint) < CONNECT_TIMEOUT) {
		// 查询网络连接信息
		sendFrame("AT+CWSTATE?\r\n"); 
		// 读取连接信息成功
		if (waitForAckU8("+CWSTATE:", 0, &wifiState)) {
			// 已经连接上 AP，并已经获取到IPv4地址
			if (wifiState == 2) {
				return 1;
			} else {
				// 其他状态，则设置wifi和密码
				sendFrame("AT+CWJAP=Gpb,Z3VvcGFuYm8=\r\n");
				if (!waitForAckStr("OK")) {
					// 设置wifi和密码超时
				}
			}
		}
	}
	return 0;
}

/*
** 建立tcp连接
** 返回：1连接成功，0连接失败，-1连接超时
*/
int8_t connectTcp(char *host, uint16_t port) {
	// 将端口号转换为字符串
	char portStr[8];
	sprintf(portStr, "%d", port);
	// 组装建立tcp连接的报文
	char cmdBuffer[RX_BYTE_BUFFER_SIZE] = "AT+CIPSTART=TCP,";
	strcat(cmdBuffer, host);
	strcat(cmdBuffer, ",");
	strcat(cmdBuffer, portStr);
	strcat(cmdBuffer, "\r\n");
	// 发送报文
	sendFrame(cmdBuffer);
	// tcp连接状态
	uint8_t tcpState = 0xFF;
	if (waitForAckU8("+CIPSTART:", 0, &tcpState)) {
		if (tcpState == 1) {
			// 已建立连接
			return 1;
		} else {
			return 0;
		}
	}
	return -1;
}

/*
** 退出透传模式
*/
void exitDataMode(void) {
	// 退出透传模式
	Delay_ms(EXIT_DATA_MODE_DELAY);
	sendFrame("+++");
	Delay_ms(EXIT_DATA_MODE_DELAY);
}

/*
** Wifi模块初始化
** 返回：1成功，-1重启失败，-2测试失败，-3等待连接超时
*/
int8_t Wifi_Init(void) {
	SysRTC_Init();
	Serial_Init();
	
	// 模块重启延时
	Delay_ms(FIRST_START_DELAY);
	
	// 先执行一下退出透传模式，以防万一，之前开启了透传模式没有关的情况
	exitDataMode();
	
	// 测试指令
	sendFrame("AT\r\n");
	if (!waitForAckStr("OK")) {
		return -2;
	}
	
	// 重启模块
	sendFrame("AT+RST\r\n");
	if (!waitForAckStr("OK") || !waitForAckStrTimeout("power on", RST_TIMEOUT)) {
		return -1;
	}
	
	// 模块重启延时
	Delay_ms(FIRST_START_DELAY);
	
	// 测试指令
	sendFrame("AT\r\n");
	if (!waitForAckStr("OK")) {
		return -2;
	}
	// 等待wifi连接
	if (!waitWifiConnected()) {
		return -3;
	}
	return 1;
}

/*
** 建立tcp连接并进入透传模式
** 返回：1连接成功，0连接失败，-1连接超时，-2设置单连接模式失败，-3进入透传模式失败
*/
int8_t Wifi_ConnectTcp(char *host, uint16_t port) {
	// 建立tcp连接
	int8_t connState = connectTcp(host, port);
	if (connState != 1) {
		return connState;
	}
	// 设置单连接模式
	sendFrame("AT+CIPMODE=1\r\n");
	if (!waitForAckStr("OK")) {
		return -2;
	}
	// 进入透传模式
	sendFrame("AT+CIPSEND\r\n");
	if (waitForAckStr("OK")) {
		if (waitForAckStr(">")) {
			// 进入透传模式成功
			return 1;
		}
	}
	// 进入透传模式失败
	return -3;
}

/*
** 退出透传模式并关闭tcp连接
** 返回：1成功，-1设置多连接模式失败，-2断开连接失败
*/
int8_t Wifi_CloseTcp(void) {
	// 退出透传模式
	exitDataMode();
	// 设置多连接模式
	sendFrame("AT+CIPMODE=1\r\n");
	if (!waitForAckStr("OK")) {
		return -1;
	}
	// 关闭连接
	sendFrame("AT+CIPCLOSE=1\r\n");
	if (!waitForAckStr("OK")) {
		return -2;
	}
	return 1;
}

/*
** 通过wifi发送数据
*/
void Tcp_SendData(char *data) {
	sendFrame(data);
}

/*
** 判断当前是否有下一个数据
** 如果有数据返回1，没有数据返回0
*/
uint8_t Tcp_hasNextData(void) {
	size_t headLen = strlen(TCP_RX_FRAME_HEAD);
	while (Serial_hasRxData()) {
		Serial_readRxData(rxData);
		// 当前报文长度
		size_t frameLen = strlen(rxData);
		// 报文头比当前整个报文长度大，则直接跳过该报文
		if (headLen > frameLen) {
			continue;
		}
		for (size_t i = 0; i < frameLen; i++) {
			// 判断报文头，是否匹配，如果不匹配，则当前报文不匹配，直接退出当前循环，继续获取下一个报文
			if (i < headLen) {
				if (rxData[i] != TCP_RX_FRAME_HEAD[i]) {
					break;
				}
			} else {
				// 当前报文是匹配的报文
				return 1;
			}
		}
	}
	return 0;
}

/*
** 读取tcp数据，将下一个数据读取到data中
*/
void Tcp_readNextData(char *data) {
	// 当前报文长度
	size_t frameLen = strlen(rxData);
	size_t headLen = strlen(TCP_RX_FRAME_HEAD);
	uint8_t offset = 0;
	for (size_t i = headLen; i < frameLen; i++) {
		// +TRDTC:1,10.10.1.25,7777,13,server test
		// 第4个数据（从0开始）是tcp报文发送过来的数据
		if (offset == 4) {
			// 将后续所有数据读取到data中
			uint8_t dataIndex = 0;
			for (size_t j = i; j < frameLen; j ++) {
				data[dataIndex++] = rxData[j];
			}
			// 最后放上字符串结尾符
			data[dataIndex] = '\0';
			return;
		}
		if (rxData[i] == ',') {
			offset ++;
		}
	}
}
